package com.kt.delivery.service.impl;

import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.kt.delivery.config.IdGen;
import com.kt.delivery.config.WechatUrlConfig;
import com.kt.delivery.config.WxPayConfig;
import com.kt.delivery.domain.*;
import com.kt.delivery.domain.vo.WxOrderVo;
import com.kt.delivery.exception.ApiException;
import com.kt.delivery.mapper.OrderMapper;
import com.kt.delivery.mapper.TakingOrderMapper;
import com.kt.delivery.mapper.UserJmLogMapper;
import com.kt.delivery.mapper.UserMapper;
import com.kt.delivery.service.OrderService;
import com.kt.delivery.utils.LoginHelper;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.exception.ValidationException;
import com.wechat.pay.java.core.notification.NotificationConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.partnerpayments.jsapi.JsapiService;
import com.wechat.pay.java.service.partnerpayments.jsapi.model.Transaction;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.payments.jsapi.model.Amount;
import com.wechat.pay.java.service.payments.jsapi.model.Payer;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayWithRequestPaymentResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
@Slf4j
public class OrderServiceImpl implements OrderService {

    @Autowired
    private WxPayConfig wxPayConfig;

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private LoginHelper loginHelper;

    @Autowired
    private TakingOrderMapper takingOrderMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private UserJmLogMapper userJmLogMapper;


    /**
     * @Description: 小程序下单
     * @Param: [userId, orders]
     * @return: java.lang.Object
     * @Author: XQD
     * @Date:2022/8/13 18:48
     */
    public Map<String,Object> createOrder(List<WxOrderVo> wxOrderVo){

        // 生成订单号
        String uuid = IdGen.getUuid();

        // 商户订单号
        String mchIdNo = IdGen.generateMerchantOrderNumber();


        // 使用自动更新平台证书的RSA配置
        // 一个商户号只能初始化一个配置，否则会因为重复的下载任务报错
        Config config =
                new RSAAutoCertificateConfig.Builder()
                        .merchantId(wxPayConfig.getMchId())
                        // 本地
                        .privateKeyFromPath(wxPayConfig.getPrivateKeyPathC())
                        // 服务器
//                        .privateKeyFromPath(wxPayConfig.getPrivateKeyPathLinux())
                        .merchantSerialNumber(wxPayConfig.getMchKey())
                        .apiV3Key(wxPayConfig.getApiV3Key())
                        .build();
        // 构建service
        JsapiServiceExtension service = new JsapiServiceExtension.Builder().config(config).build();
        // request.setXxx(val)设置所需参数，具体参数可见Request定义
        PrepayRequest request = new PrepayRequest();
        Amount amount = new Amount();

        BigDecimal total = BigDecimal.valueOf(0);
        for (WxOrderVo orderVo : wxOrderVo) {
            total = total.add(orderVo.getMoney());
        }
        amount.setTotal(total.intValue());
        request.setAmount(amount);
        request.setAppid(wxPayConfig.getAppId());
        request.setMchid(wxPayConfig.getMchId());
        request.setDescription("测试商品标题");
        request.setNotifyUrl(WechatUrlConfig.notifyUrl);
        request.setOutTradeNo(mchIdNo);
        Payer payer = new Payer();
        payer.setOpenid(loginHelper.getOpenId());
        request.setPayer(payer);

        // 调用下单方法，得到应答
        PrepayWithRequestPaymentResponse response = service.prepayWithRequestPayment(request);

        // 添加数据库订单
        for (WxOrderVo orderVo : wxOrderVo) {
            Order order  = new Order();
            BeanUtils.copyProperties(orderVo,order);
            order.setUserId(loginHelper.getUserId());
            order.setPaymentStatus(0L);
            order.setOrderId(uuid);
            order.setOutTradeNo(mchIdNo);
            // 以分为单位 1/100=0.01 0.01是实际支付的钱
            order.setMoney(orderVo.getMoney().divide(new BigDecimal(100)));
            order.setCreateTime(LocalDate.now());

            orderMapper.insert(order);
        }


        Map<String,Object> map = new HashMap<>();

        map.put("appId",response.getAppId());
        map.put("timeStamp",response.getTimeStamp());
        map.put("nonceStr",response.getNonceStr());
        map.put("packageVal",response.getPackageVal());
        map.put("signType",response.getSignType());
        map.put("paySign",response.getPaySign());

        return map;
    }


    // 金币
    int jinbi;

    /**
     * 充值金币
     * @return
     */
    @Override
    public Map<String, Object> updateMoney(int jm , int money) {

        jinbi = jm;

        // 生成订单号
        String uuid = IdGen.getUuid();

        // 使用自动更新平台证书的RSA配置
        // 一个商户号只能初始化一个配置，否则会因为重复的下载任务报错
        Config config =
                new RSAAutoCertificateConfig.Builder()
                        .merchantId(wxPayConfig.getMchId())
                        // 本地
                        .privateKeyFromPath(wxPayConfig.getPrivateKeyPathC())
                        // 服务器
//                        .privateKeyFromPath(wxPayConfig.getPrivateKeyPathLinux())
                        .merchantSerialNumber(wxPayConfig.getMchKey())
                        .apiV3Key(wxPayConfig.getApiV3Key())
                        .build();
        // 构建service
        JsapiServiceExtension service = new JsapiServiceExtension.Builder().config(config).build();
        // request.setXxx(val)设置所需参数，具体参数可见Request定义
        PrepayRequest request = new PrepayRequest();
        Amount amount = new Amount();
        amount.setTotal(jm);
        request.setAmount(amount);
        request.setAppid(wxPayConfig.getAppId());
        request.setMchid(wxPayConfig.getMchId());
        request.setDescription("用户充值金币");
        request.setNotifyUrl(WechatUrlConfig.notifyUrlJm);
        request.setOutTradeNo(uuid);
        Payer payer = new Payer();
        payer.setOpenid(loginHelper.getOpenId());
        request.setPayer(payer);

        // 调用下单方法，得到应答
        PrepayWithRequestPaymentResponse response = service.prepayWithRequestPayment(request);

        UserJmLog userJmLog = new UserJmLog();

        // 新增金币充值记录
        userJmLog.setMoney(BigDecimal.valueOf(money).divide(new BigDecimal(100)));
        userJmLog.setUserId(loginHelper.getUserId());
        userJmLog.setCreateTime(LocalDateTime.now());
        userJmLog.setTransactionId(null);
        userJmLog.setOutTradeNo(uuid);
        userJmLog.setStatus(0L);
        userJmLog.setJm((long) jm);
        userJmLogMapper.insert(userJmLog);


        Map<String,Object> map = new HashMap<>();

        map.put("appId",response.getAppId());
        map.put("timeStamp",response.getTimeStamp());
        map.put("nonceStr",response.getNonceStr());
        map.put("packageVal",response.getPackageVal());
        map.put("signType",response.getSignType());
        map.put("paySign",response.getPaySign());

        return map;
    }


    /**
     * 下单支付回调结果
     * @param request
     * @return
     * @throws IOException
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String payNotify(HttpServletRequest request) throws IOException {
        log.info("------下单收到支付通知------");
        // 请求头Wechatpay-Signature
        String signature = request.getHeader("Wechatpay-Signature");
        // 请求头Wechatpay-nonce
        String nonce = request.getHeader("Wechatpay-Nonce");
        // 请求头Wechatpay-Timestamp
        String timestamp = request.getHeader("Wechatpay-Timestamp");
        // 微信支付证书序列号
        String serial = request.getHeader("Wechatpay-Serial");
        // 签名方式
        String signType = request.getHeader("Wechatpay-Signature-Type");

        // 构造RequestParam
        RequestParam requestParam = new RequestParam.Builder()
                .serialNumber(serial)
                .nonce(nonce)
                .signature(signature)
                .timestamp(timestamp)
                .signType(signType)
                .body(getRequestBody(request))
                .build();

        // 如果已经初始化了 RSAAutoCertificateConfig，可直接使用
        // 没有的话，则构造一个
        NotificationConfig config = new RSAAutoCertificateConfig.Builder()
                .merchantId(wxPayConfig.getMchId())
                // 本地
              .privateKeyFromPath(wxPayConfig.getPrivateKeyPathC())
                // 服务器
//                .privateKeyFromPath(wxPayConfig.getPrivateKeyPathLinux())
                .merchantSerialNumber(wxPayConfig.getMchKey())
                .apiV3Key(wxPayConfig.getApiV3Key())
                .build();

        NotificationParser notificationParser = new NotificationParser(config);

        Transaction parse = notificationParser.parse(requestParam, Transaction.class);
        log.info("-----------------------parse = " + parse);

        try {

//            List<Order> orderList = orderMapper.selectList(new QueryWrapper<Order>().eq("out_trade_no", parse.getOutTradeNo().toString()));
            if (parse.getTradeState().equals(Transaction.TradeStateEnum.SUCCESS)){
                // 修改订单状态
                updateAccountDetail(parse);
            }

        } catch (Exception e) {
            throw new ApiException("支付取消","302");
        }
        log.info("-----------------------回调完成-----------------------");
        return JSONUtil.toJsonStr(parse);

    }

    /**
     * 金币充值支付回调结果
     * @param request
     * @return
     * @throws IOException
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String payNotifyJm(HttpServletRequest request) throws IOException {

        log.info("------金币支付 -收到支付通知------");
        // 请求头Wechatpay-Signature
        String signature = request.getHeader("Wechatpay-Signature");
        // 请求头Wechatpay-nonce
        String nonce = request.getHeader("Wechatpay-Nonce");
        // 请求头Wechatpay-Timestamp
        String timestamp = request.getHeader("Wechatpay-Timestamp");
        // 微信支付证书序列号
        String serial = request.getHeader("Wechatpay-Serial");
        // 签名方式
        String signType = request.getHeader("Wechatpay-Signature-Type");

        // 构造RequestParam
        RequestParam requestParam = new RequestParam.Builder()
                .serialNumber(serial)
                .nonce(nonce)
                .signature(signature)
                .timestamp(timestamp)
                .signType(signType)
                .body(getRequestBody(request))
                .build();

        // 如果已经初始化了 RSAAutoCertificateConfig，可直接使用
        // 没有的话，则构造一个
        NotificationConfig config = new RSAAutoCertificateConfig.Builder()
                .merchantId(wxPayConfig.getMchId())
                // 本地
              .privateKeyFromPath(wxPayConfig.getPrivateKeyPathC())
                // 服务器
//                .privateKeyFromPath(wxPayConfig.getPrivateKeyPathLinux())
                .merchantSerialNumber(wxPayConfig.getMchKey())
                .apiV3Key(wxPayConfig.getApiV3Key())
                .build();

        NotificationParser notificationParser = new NotificationParser(config);

        Transaction parse = notificationParser.parse(requestParam, Transaction.class);
        log.info("-----------------------parse = " + parse);

            // 查询是否已经支付过了
            UserJmLog userJmLog = userJmLogMapper.selectOne(new QueryWrapper<UserJmLog>().eq("out_trade_no", parse.getOutTradeNo()));
            if (userJmLog.getStatus() == 1){
                return "success";
            }
            if (parse.getTradeState().equals(Transaction.TradeStateEnum.SUCCESS)){
                updateJm(parse);
            }
        log.info("-----------------------回调完成-----------------------");
        return JSONUtil.toJsonStr(parse);

    }

    /**
     * 修改订单状态
     * @param parse
     */
    @Transactional(rollbackFor = Exception.class)
    public void updateAccountDetail(Transaction parse) {

        String outTradeNo = parse.getOutTradeNo();

        // 根据outTradeNo查询订单信息
        List<Order> orderList = orderMapper.selectList(new QueryWrapper<Order>().eq("out_trade_no", outTradeNo));

        for (Order order : orderList) {
            // 修改订单状态
            orderMapper.update(new UpdateWrapper<Order>().set("payment_status",1).eq("out_trade_no",order.getOutTradeNo()));

            // 新增接单员的订单信息
            TakingOrder takingOrder = new TakingOrder();
            takingOrder.setOrderId(order.getOrderId());
            takingOrder.setAddress(order.getAddress());
            takingOrder.setOutTradeNo(order.getOutTradeNo());
            takingOrder.setClaimCode(order.getClaimCode());
            takingOrder.setMoney(order.getMoney());
            takingOrder.setRemark(order.getRemark());
            takingOrder.setPhone(order.getPhone());
            takingOrder.setPickStatus(order.getPickStatus());
            takingOrder.setStatus(10L);
            takingOrder.setSpecification(order.getSpecification());
            takingOrder.setStation(order.getStation());
            takingOrder.setCreateTime(LocalDate.now());
            takingOrderMapper.insert(takingOrder);
        }
    }

    public void updateJm(Transaction parse){
        // 修改金币支付记录表的状态
        userJmLogMapper.update(new UpdateWrapper<UserJmLog>()
                .set("status",1)
                .set("transaction_id",parse.getTransactionId()).eq("out_trade_no",parse.getOutTradeNo()));

        // 根据支付记录表用户id改变user表中的余额

        UserJmLog userJmLog = userJmLogMapper.selectOne(new QueryWrapper<UserJmLog>().eq("out_trade_no", parse.getOutTradeNo()));


        // 修改用户充值的金币
        User user = userMapper.selectById(userJmLog.getUserId());
        log.info("用户信息:"+user);
        BigDecimal total = BigDecimal.valueOf(0);

        total = user.getJm().add(BigDecimal.valueOf(jinbi));
        log.info("金币:"+total);
        userMapper.update(new UpdateWrapper<User>().set("jm",total).eq("id",user.getId()));
    }
    /**
     * 获取请求体
     *
     * @param request
     * @return
     * @throws IOException
     */
    public static String getRequestBody(HttpServletRequest request) throws IOException {
        ServletInputStream stream = null;
        BufferedReader reader = null;
        StringBuffer sb = new StringBuffer();
        try {
            stream = request.getInputStream();
            // 获取响应
            reader = new BufferedReader(new InputStreamReader(stream));
            String line;
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            throw new IOException("读取返回支付接口数据流出现异常！");
        } finally {
            reader.close();
        }
        return sb.toString();
    }

}
